package dispatcher

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"dev.ixmxm.top/rms/boot"
	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/managers/robotmanager"
	"dev.ixmxm.top/rms/model"
	"dev.ixmxm.top/rms/proto"
	"dev.ixmxm.top/rms/utils/base64"
	"dev.ixmxm.top/rms/utils/json"
	"dev.ixmxm.top/rms/utils/log"
	"dev.ixmxm.top/rms/utils/tools"
	"github.com/gin-gonic/gin"
)

//小车获取地图
func (m *DispatchManager) QueryMaps(ctx context.Context, request *proto.GetMapsRequest) (*proto.GetMapsRequestReply, error) {
	// log.Info("robot query maps")
	maps, errGetMaps := m.mc.TRM.GetAllMapFromDB(ctx)
	if errGetMaps != nil {
		log.ErrorfWithContext(ctx, "get all maps error:%s", errGetMaps.UnWrapError())
		return &proto.GetMapsRequestReply{Code: errGetMaps.GetCode(), Message: errGetMaps.Error()}, nil
	}
	if len(maps) == 0 {
		return &proto.GetMapsRequestReply{Code: codes.ErrMapNotFound.GetCode(), Message: codes.ErrMapNotFound.Error()}, nil
	}
	r := robotmanager.GetFromCache(request.GetAgvId())
	if r == nil {
		reply := new(proto.GetMapsRequestReply)
		reply.Code = codes.ErrRobotNotExists.GetCode()
		reply.Message = codes.ErrRobotNotExists.Error()
		return reply, nil
	}
	mapPbs := make([]*proto.MapPb, 0)
	robotMaps := []string{}
	if r.Account.GroupID != "" {
		accountGroup, isOk := robotmanager.GetRobotGroup(r.Account.GroupID)
		if !isOk {
			return &proto.GetMapsRequestReply{Code: codes.ErrRobotGroupNotExists.GetCode(), Message: codes.ErrRobotGroupNotExists.Error()}, nil
		}
		if accountGroup.MapList != nil {
			robotMaps = accountGroup.MapList
		}
	} else { //下发关联地图
		var errRelation codes.Code
		robotMaps, errRelation = m.mc.TRM.GetMapAndRelationDeeply(ctx, r.Account.MapName)
		if errRelation != nil {
			return &proto.GetMapsRequestReply{Code: errRelation.GetCode(), Message: errGetMaps.Error()}, nil
		}
	}

	for _, item := range maps {
		itMap := item
		if tools.StringSliceIsContain(robotMaps, itMap.MapName) || r.Account.MapName == itMap.MapName {
			mapPbs = append(mapPbs, &proto.MapPb{ProjectId: itMap.ProjectId, TrafficMapName: itMap.TrafficName, Data: itMap.RawBase64.String()})
		}
	}

	reply := new(proto.GetMapsRequestReply)
	reply.Code = 0
	reply.Message = "success"
	reply.Maps = mapPbs
	return reply, nil
}

//小车获取厂区楼层
func (m *DispatchManager) QueryAreaAndFloor(ctx context.Context, request *proto.QueryAreaAndFloorRequest) (*proto.QueryAreaAndFloorReply, error) {
	reply := new(proto.QueryAreaAndFloorReply)

	r := robotmanager.GetFromCache(request.GetAgvId())
	if r == nil {
		reply.Code = codes.ErrRobotNotExists.GetCode()
		reply.Message = codes.ErrRobotNotExists.Error()
		return reply, nil
	}
	if r.Account.MapName == "" {
		reply.Code = 1
		reply.Message = "failed"
		return reply, nil
	}
	elems := strings.Split(r.Account.MapName, "@")
	reply.Code = 0
	reply.Message = "success"
	reply.ProjectId = elems[0]
	reply.TrafficMapName = elems[1]
	return reply, nil
}

//小车上传错误码
func (m *DispatchManager) RecordErrorCode(ctx context.Context, request *proto.RmsRecordErrorCode) (*proto.ReplyPb, error) {
	if err := m.mc.LM.AddRobotLogToDB(ctx, &model.RobotLog{Code: request.GetErrCode(), Msg: request.GetErrMsg(), AgvID: request.GetAgvId(), StartTime: request.GetStartTime()}); err != nil {
		log.ErrorfWithContext(ctx, "add robot log error:%s", err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}

//小车获取配置信息
func (m *DispatchManager) GetConfig(ctx context.Context, request *proto.RmsGetConfig) (*proto.ReplyConfig, error) {
	reply := new(proto.ReplyConfig)
	r := robotmanager.GetFromCache(request.GetAgvId())
	if r == nil {
		reply.Code = codes.ErrRobotNotExists.GetCode()
		reply.Message = codes.ErrRobotNotExists.Error()
		return reply, nil
	}
	reply.Code = 0
	reply.Message = "success"
	tools.Copy(r.Type, reply)
	// log.Infof("robot config:%s", json.DumpString(r.Type))
	// log.Infof("robot config pb:%s", json.DumpString(reply))
	return reply, nil
}

//小车上传状态信息
func (m *DispatchManager) UploadRobotStatus(server proto.RMSServer_UploadRobotStatusServer) error {
	log.Info("start receive robot status")
	for {
		select {
		case <-server.Context().Done():
			log.Warn("stop receive robot status")
			return nil
		default:
			msg, err := server.Recv()
			if err != nil {
				log.Warn("receive robot status error:", err.Error())
				return err
			}
			robot := robotmanager.GetFromCache(msg.GetRobotStatus().GetAgvId())
			if robot == nil {
				// log.Warn("update robot status failed,robot not found:", msg.GetRobotStatus().GetAgvId())
				continue
			}
			//机器人禁用不接收状态
			if !robot.Account.IsEnable {
				continue
			}

			//log.Info("start receive robot status****Online:", "*********  robot:", robot.Account.AgvId)

			caller, find := m.GetCaller(msg.GetRobotStatus().GetAgvId())
			if !find {
				// log.Warn("update robot status failed,call not found:", msg.GetRobotStatus().GetAgvId())
				continue
			}
			status := new(model.RobotStatus)
			tools.Copy(msg.GetRobotStatus(), status)
			status.SiteIdS = strconv.Itoa(status.SiteId)
			if status.Angle < 0 {
				status.Angle += 360
			}
			if status.SiteIdS != "0" {
				robot.LastSiteId = status.SiteIdS
			}

			//log.Warn("receive robot%s status %+v", msg.GetRobotStatus().GetAgvId(), status.ToString())
			//新增功能 如果自动模式下，车载上报地图与调度配置不一致，持续下发切换指令
			if status.ControlType == 1 && robot.Account.MapName != status.MapName &&
				status.Status != model.RobotStatusInit && robot.Account.MapName != "" {
				elems := strings.Split(robot.Account.MapName, "@")
				if len(elems) == 2 {
					caller.SwitchMap(context.Background(), elems[0], elems[1], "0", "0")
				}
			}

			go caller.statusCallback(status)
		}
	}
}

//小车心跳
func (m *DispatchManager) UploadHeartBeat(heartBeat *proto.HeartBeat, server proto.RMSServer_UploadHeartBeatServer) error {
	//log.Info("接收掉线验证")

	//增加机器人状态不对返回错误
	robot := robotmanager.GetFromCache(heartBeat.HeartString)
	if robot == nil {
		return codes.ErrRobotNotExists
	}
	if !robot.Account.IsEnable {
		return codes.ErrRobotDisable
	}
	//解决小车在线、任务不在线情况，让车载重新调用taskonline
	// if robot.Status != nil && !robot.Status.TaskOnline && robot.Status.Online {
	// 	caller, find := m.callers[robot.Account.AgvId]
	// 	if !find {
	// 		log.Warn("update robot status failed,call not found:", robot.Account.AgvId)
	// 		return codes.ErrRobotNotExists
	// 	}
	// 	status := new(model.RobotStatus)
	// 	tools.Copy(robot.Status, status)
	// 	status.Online = false
	// 	status.TaskOnline = false
	// 	caller.statusCallback(status)
	// 	return codes.ErrDeviceStatusNotMatch
	// }

	// 向流中发送消息， 默认每次send送消息最大长度为`math.MaxInt32`bytes
	err := server.Send(&proto.HeartBeat{
		HeartString: heartBeat.HeartString,
	})
	if err != nil {
		return err
	}
	return nil
}

func (m *DispatchManager) AreaAndFloor(ctx context.Context, request *proto.RmsAreaAndFloorRequest) (*proto.ReplyPb, error) {
	reply := &proto.ReplyPb{Code: 0, Message: "success"}
	map_name := m.mc.TRM.GetMapName(request.ProjectId, request.TrafficMapName)
	if map_name == "" {
		return &proto.ReplyPb{Code: codes.ErrMapNotFound.GetCode(), Message: codes.ErrMapNotFound.Error()}, nil
	}
	if err := robotmanager.SetMapNameToDB(ctx, request.GetAgvId(), map_name); err != nil {
		reply.Code = err.GetCode()
		reply.Message = err.Error()
		return reply, nil
	}
	return reply, nil
}

//写入碰撞AGV
func (m *DispatchManager) WriteConflictAgv(ctx context.Context, subtask *model.Subtask, blockAgv string, setPath, isSetBlockAgv bool, gPath *model.GlobalPath) {
	if blockAgv != "" {
		blockSubtask, errGetTask := m.mc.TM.GetRunningSubtaskFromDB(ctx, blockAgv)
		blockRobot := robotmanager.GetFromCache(blockAgv)
		if blockRobot.Status != nil {
			record_time := time.Now()
			if subtask.BlockAgv != "" {
				record_time = subtask.BlockStartTime
			}
			if gPath != nil && setPath {
				cnt := 0
				for index, p := range gPath.Paths {
					if gPath.BlockStart == p.PathId {
						cnt = index
						break
					}
				}
				tmpPaths := gPath.Paths[cnt:]
				m.mc.TM.UpdateSubtaskLeftPathToDB(ctx, subtask.Id, "paths", &tmpPaths)
			}
			m.TRM().UpdateFailedPlanAGVToDB(ctx, subtask.AgvId, blockAgv)
			if subtask.BlockAgv == blockAgv && subtask.BlockAgvSite == blockRobot.Status.SiteIdS {
				return
			}
			if errGetTask == nil {
				m.mc.TM.UpdateSubtaskBlockInfoToDB(ctx, subtask.Id, blockAgv, blockRobot.Status.SiteIdS, blockSubtask.Id, int32(blockSubtask.Grade), record_time, 0)
				log.Warnf("堵塞信息，blockAgv= %s blockSubtask.Id=%d blockSubtask.Grade=%d", blockAgv, blockSubtask.Id, blockSubtask.Grade)
			} else if !m.SystemConfig.TrafficCfg.UseIdleAvoid {
				m.mc.TM.UpdateSubtaskBlockInfoToDB(ctx, subtask.Id, blockAgv, blockRobot.Status.SiteIdS, 0, 99, record_time, 0)
				log.Warnf("堵塞信息，blockAgv= %s AGV空闲", blockAgv)
			} else if isSetBlockAgv {
				m.mc.TM.UpdateSubtaskBlockInfoToDB(ctx, subtask.Id, blockAgv, blockRobot.Status.SiteIdS, 0, 99, record_time, 0)
				log.Warnf("堵塞信息，blockAgv= %s AGV重发记录", blockAgv)
			}
		}
	} else if subtask.BlockAgv != "" {
		m.mc.TM.UpdateSubtaskBlockInfoToDB(ctx, subtask.Id, "", "0", 0, 0, time.Now(), 0)
		m.mc.TM.UpdateSubtaskLeftPathToDB(ctx, subtask.Id, "paths", nil)
		m.TRM().UpdateFailedPlanAGVToDB(ctx, subtask.AgvId, blockAgv)
	}
}

//小车路径完成上传
func (m *DispatchManager) Reach(ctx context.Context, request *proto.ReachRequest) (*proto.ReplyPb, error) {
	robotId := request.GetAgvId()
	robot := robotmanager.GetFromCache(robotId)
	mapName := fmt.Sprintf("%s@%s", request.GetProjectId(), request.GetTrafficMapName())
	pathId := strconv.Itoa(int(request.GetPathId()))
	getSiteId := strconv.Itoa(int(request.GetSiteId()))
	requestType := request.GetType()
	log.Infof("到达: robot:%s,reach:%s,type:%d", robotId, pathId, requestType)
	//未收到小车连接和小车状态数据时，返回错误
	if !robot.StatusIsExists() {
		log.Warn("robot reach, but robot not found:", robotId)
		return &proto.ReplyPb{Code: codes.ErrRobotNotExists.GetCode(), Message: codes.ErrRobotNotExists.Error()}, nil
	}
	//AGV状态为空闲和待命时，返回错误
	if robot.Status.Status == model.RobotStatusIdle || robot.Status.Status == model.RobotStatusStandby {
		log.Warn("AGV状态为空闲或待命时，不接受小车到达信号上报。robot:", robotId, "   pathId:", request.PathId)
		return &proto.ReplyPb{Code: codes.ErrRobotNotExists.GetCode(), Message: "AGV状态为空闲或待命时，不接受小车到达信号上报。"}, nil
	}

	subtask, errGetTask := m.mc.TM.GetRunningSubtaskFromDB(ctx, robotId)
	_, _, localRejects := m.mc.MakePlanRejectNodes(robotId, mapName, robot.AgvCorverNodes, subtask)
	if errGetTask != nil {

		//解决任务先完成，路径未删除，是由于先调用taskdone，再调用reach导致的
		m.normalTaskLock[robot.Account.MapName].Lock()
		m.normalTaskDoingFlag[robot.Account.MapName].IsOccupy(true)
		startTime := time.Now()
		defer func() {
			log.Infof("robot:%s handle reach time %dms", robotId, time.Now().UnixMilli()-startTime.UnixMilli())
			m.normalTaskDoingFlag[robot.Account.MapName].IsOccupy(false)
			m.normalTaskLock[robot.Account.MapName].Unlock()
		}()
		gPaths, lPaths, isFinal, blockAgv, err := m.mc.TRM.Reach(ctx, robot, mapName, pathId, requestType, localRejects)
		if err != nil {
			log.Errorf("robot:%s reach failed:%s", robotId, err.Error())
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}
		switch requestType {
		case proto.IdType_StartRotate, proto.IdType_EndRotate:
			return &proto.ReplyPb{Code: 0, Message: "success"}, nil
		}
		if gPaths == nil && lPaths == nil && isFinal {
			//任务完成
			return &proto.ReplyPb{Code: 0, Message: "success"}, nil
		}
		if (gPaths != nil && lPaths == nil) || gPaths == nil && lPaths == nil && !isFinal {
			//局部路径规划失败或到达中转点
			if blockAgv != "" && subtask != nil {
				blockSubtask, errGetTask := m.mc.TM.GetRunningSubtaskFromDB(ctx, blockAgv)
				blockRobot := robotmanager.GetFromCache(blockAgv)
				if errGetTask != nil && blockRobot.StatusIsExists() {
					m.mc.TM.UpdateSubtaskBlockInfoToDB(ctx, subtask.Id, blockAgv, blockRobot.Status.SiteIdS, blockSubtask.Id, int32(blockSubtask.Grade), time.Now(), 0)
					log.Warnf("堵塞信息，blockAgv= %s blockSubtask.Id=%d blockSubtask.Grade=%d", blockAgv, blockSubtask.Id, blockSubtask.Grade)
				}
			}
			m.mc.TRM.DeleteRobotGlobalPaths(ctx, robotId, mapName)
			m.mc.TRM.AddFailedPlanToDB(ctx, robotId, blockAgv, robot.Account.MapName, model.PathPlanFailed)
			if subtask != nil {
				m.mc.TM.UpdateSubtaskKeyStringToDB(ctx, subtask.Id, "avoid_site", "")
			}
			log.Warnf("堵塞，发送清空任务到AGV，任务扔到FailedPlan")
			return &proto.ReplyPb{Code: 0, Message: "success"}, nil
		}
		log.Warn("robot reach, but robot task not found:", robotId)
		return &proto.ReplyPb{Code: errGetTask.GetCode(), Message: errGetTask.Error()}, nil
	}
	if pathId == "666666" {
		log.Infof("收到robot:%s 提前到达信号666666,提前结束任务taskid:%d, subtaskid:%d", robotId, subtask.TaskId, subtask.Id)
		ctxTx := boot.Begin(ctx)
		if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, subtask.Id, model.TaskStatusFinish, false); err != nil {
			log.Errorf("finish task error:%s", err.Error())
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}
		ctxTx.Commit()
		log.Infof("robot:%s 结束任务taskid:%d, subtaskid:%d 成功", robotId, subtask.TaskId, subtask.Id)
		if robot.Type.CategoryType == model.CategoryTypeStocker && robot.Status.SiteIdS == "0" {
			robot.Status.SiteIdS = getSiteId
			log.Infof("robot:%s 点位更新为:%s", robotId, getSiteId)
		}
		return &proto.ReplyPb{Code: 0, Message: "success"}, nil
	}

	if getSiteId == subtask.AvoidSite && (subtask.SubStatus == model.WalkForwardAvoid || subtask.SubStatus == model.WalkBackforwardAvoid || subtask.SubStatus == model.WalkNearAvoid) {
		subtask.SubStatus = model.WalkAvoidFinish
		subtask.BlockAvoidTime = time.Now()
		log.Infof("到达%s 更新任务子状态为清除完成 model.WalkAvoidFinish", getSiteId)
		m.mc.TM.UpdateSubtaskToDB(ctx, subtask)
	} else if getSiteId == subtask.TempTargetSite && (subtask.SubStatus == model.WalkToTemp || subtask.SubStatus == model.WalkToTargetReplace || subtask.SubStatus == model.WalkToTempReplace) {
		subtask.SubStatus = model.WalkTempFinish
		subtask.BlockAvoidTime = time.Now()
		log.Infof("到达%s 更新任务子状态为清除完成 model.WalkTempFinish", getSiteId)
		m.mc.TM.UpdateSubtaskToDB(ctx, subtask)
	} else if subtask.SubStatus == model.WalkToTarget {
		subtask.BlockAvoidTime = time.Now()
		m.mc.TM.UpdateSubtaskToDB(ctx, subtask)
	}

	m.normalTaskLock[robot.Account.MapName].Lock()
	m.normalTaskDoingFlag[robot.Account.MapName].IsOccupy(true)
	startTime := time.Now()
	defer func() {
		log.Infof("robot:%s handle reach time %dms", robotId, time.Now().UnixMilli()-startTime.UnixMilli())
		m.normalTaskDoingFlag[robot.Account.MapName].IsOccupy(false)
		m.normalTaskLock[robot.Account.MapName].Unlock()
	}()
	//任务状态为取消时，返回错误
	if subtask.Status == model.TaskStatusCanceled || subtask.Status == model.TaskStatusCanceling {
		log.Warnf("任务状态为取消时，不接受小车到达信号上报。robot:", robotId, "   subtaskid:", subtask.Id)
		return &proto.ReplyPb{Code: codes.ErrTaskNotExists.GetCode(), Message: "任务状态为取消时，不接受小车到达信号上报。"}, nil
	} else if subtask.SubStatus == model.WalkTaskClearing {
		log.Warnf("任务子状态为取消时，不接受小车到达信号上报。robot:", robotId, "   subtaskid:", subtask.Id)
		return &proto.ReplyPb{Code: codes.ErrTaskNotExists.GetCode(), Message: "任务子状态为取消时，不接受小车到达信号上报。"}, nil
	}
	gPaths, lPaths, isFinal, blockAgv, err := m.mc.TRM.Reach(ctx, robot, mapName, pathId, requestType, localRejects)
	if err != nil && err != codes.ErrCallDevice {
		log.Errorf("robot:%s reach failed:%s", robotId, err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	switch requestType {
	case proto.IdType_StartRotate, proto.IdType_EndRotate:
		return &proto.ReplyPb{Code: 0, Message: "success"}, nil
	}
	if gPaths == nil && lPaths == nil && isFinal {
		//任务完成
		return &proto.ReplyPb{Code: 0, Message: "success"}, nil
	}
	if (gPaths != nil && lPaths == nil) || gPaths == nil && lPaths == nil && !isFinal {
		//局部路径规划失败或到达中转点
		m.WriteConflictAgv(ctx, subtask, blockAgv, true, false, gPaths)
		m.mc.TRM.DeleteRobotGlobalPaths(ctx, robotId, mapName)
		m.mc.TRM.AddFailedPlanToDB(ctx, robotId, blockAgv, robot.Account.MapName, model.PathPlanFailed)
		m.mc.TM.UpdateSubtaskKeyStringToDB(ctx, subtask.Id, "avoid_site", "")
		log.Warnf("robot%s堵塞，任务扔到FailedPlan", robotId)
		return &proto.ReplyPb{Code: 0, Message: "success"}, nil
	}

	if subtask.TaskOperateFlg == 0 {
		caller, find := m.callers[robot.Account.AgvId]
		if !find {
			log.Errorf("robot caller not found:%s", robot.Account.AgvId)
			return &proto.ReplyPb{Code: codes.ErrCallService.GetCode(), Message: codes.ErrCallService.Error()}, nil
		}
		if errCall := caller.RunTask(ctx, subtask, gPaths, lPaths, robot, m); errCall != nil {
			return &proto.ReplyPb{Code: codes.ErrCallService.GetCode(), Message: codes.ErrCallService.Error()}, nil
		}
	}
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}

//小车任务上报
func (m *DispatchManager) TaskDone(ctx context.Context, request *proto.TaskDoneRequest) (*proto.ReplyPb, error) {
	if request.GetTaskId() == 0 {
		return &proto.ReplyPb{Code: 0, Message: "success"}, nil
	}
	subtask, errGetTask := m.mc.TM.GetSubtaskFromDB(ctx, request.GetTaskId())
	if errGetTask != nil {
		log.Errorf("finish task error,get task error:%s", errGetTask.Error())
		return &proto.ReplyPb{Code: errGetTask.GetCode(), Message: errGetTask.Error()}, nil
	}
	robotmanager.UpdateTaskFlgToCache(subtask.AgvId, false)
	log.Infof("robot:%s receive task done:%d", subtask.AgvId, request.GetTaskId())
	switch subtask.Type {
	case model.TaskTypeCharge:
		// //操作充电桩开启
		// if errStartCharge := m.mc.CM.StartRobotCharger(ctx, t); errStartCharge != nil {
		// 	log.Errorf("start charge error:%s", errStartCharge.Error())
		// 	m.mc.TM.UpdateStatusToDB(ctx, request.GetTaskId(), model.TaskStatusError)
		// 	return &proto.ReplyPb{Code: errStartCharge.GetCode(), Message: errStartCharge.Error()}, nil
		// }

	case model.TaskTypeStandBy:
	default:
		if errAddEffiency := m.mc.TM.AddEfficency(ctx, subtask.AgvId, subtask.StartTime, time.Now()); errAddEffiency != nil {
			log.Errorf("add task:%d effiency error:%s", subtask.Id, errAddEffiency.Error())
		}
	}

	ctxTx := boot.Begin(ctx)
	//充电任务改为充电中
	if subtask.Type == model.TaskTypeCharge {
		if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, subtask.Id, model.TaskStatusCharging, false); err != nil {
			log.Errorf("finish Chargtask error:%s", err.Error())
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}
		ctxTx.Commit()
		return &proto.ReplyPb{Code: 0, Message: "success"}, nil
	}
	if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, subtask.Id, model.TaskStatusFinish, false); err != nil {
		log.Errorf("finish task error:%s", err.Error())
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	if err := m.exchangeTask(ctxTx, subtask); err != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	ctxTx.Commit()

	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}

//小车任务操作
func (m *DispatchManager) Operate(ctx context.Context, request *proto.OperateRequest) (*proto.DeviceReplyPb, error) {
	log.Infof("receive operate device command:%s,device id:%s", request.GetOperateCmd(), request.GetDeviceCode())
	// reply, err := m.mc.DM.OperateRPC(request)
	// if err != nil {
	// 	return &proto.DeviceReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	// }
	reply := &proto.DeviceReplyPb{
		Code:         0,
		Message:      "success",
		Online:       true,
		Enable:       true,
		DeviceStatus: 0}
	return reply, nil
}

func (m *DispatchManager) TaskOperateFeedback(ctx context.Context, request *proto.TaskOperateFeedbackRequest) (*proto.ReplyPb, error) {
	log.Infof("接收小车返回的任务操作 taskid:%d,operate code:%d", request.GetTaskId(), int(request.GetOperateCmd()))
	getSiteId := strconv.Itoa(int(request.GetSiteId()))
	var status model.TaskStatus
	switch request.GetOperateCmd() {
	case proto.TaskOperatePb_TaskPause:
		status = model.TaskStatusPaused
	case proto.TaskOperatePb_TaskContinue:
		status = model.TaskStatusRunning
	case proto.TaskOperatePb_TaskCancel:
		status = model.TaskStatusCanceled
	case proto.TaskOperatePb_TaskFinish:
		status = model.TaskStatusFinish
	case proto.TaskOperatePb_TaskClear:
		status = model.TaskStatusRunning
	}
	subtask, errGetTask := m.mc.TM.GetSubtaskFromDB(ctx, request.GetTaskId())
	if errGetTask != nil {
		return &proto.ReplyPb{Code: errGetTask.GetCode(), Message: errGetTask.Error()}, nil
	}
	// m.mc.TM.UpdateSubtaskKeyToDB(ctx, subtask.Id, "task_operate_flag", 0)
	ctxTx := boot.Begin(ctx)
	if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, subtask.Id, status, false); err != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	switch status {
	case model.TaskStatusCanceled, model.TaskStatusFinish:
		//先修改其余子任务状态
		if status == model.TaskStatusFinish {
			//先修改其余子任务状态为已完成
			if err1 := m.mc.TM.DeleteSubtasksToDB(ctxTx, subtask.TaskId); err1 != nil {
				ctxTx.Rollback()
				return &proto.ReplyPb{Code: err1.GetCode(), Message: err1.Error()}, nil
			}
		}
		//清除路径
		r := robotmanager.GetFromCache(subtask.AgvId)
		if !r.StatusIsExists() {
			log.Errorf("robot status is nil")
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: codes.ErrDeviceStatusNotExists.GetCode(), Message: codes.ErrDeviceStatusNotExists.Error()}, nil
			//return codes.ErrDeviceStatusNotExists
		}
		if err := m.mc.TRM.ClearRobotPathAndLockNode(ctxTx, r, subtask.MapName, getSiteId, r.Status.Angle, r.Type); err != nil {
			if errors.Is(err, codes.ErrLockConflict) {
				caller, _ := m.GetCaller(r.Account.AgvId)
				if caller == nil {
					log.Infof("没有找到小车连接")
					ctxTx.Rollback()
				}
				if errAlarm := caller.SendBlockConflictAlarm(ctxTx, int(request.GetSiteId())); errAlarm != nil {
					ctxTx.Rollback()
					return &proto.ReplyPb{Code: errAlarm.GetCode(), Message: errAlarm.Error()}, nil
				}
				ctxTx.Commit()
				return &proto.ReplyPb{Code: 0, Message: "success"}, nil
			}
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}
	}
	if request.GetOperateCmd() == proto.TaskOperatePb_TaskClear {
		//清除路径
		r := robotmanager.GetFromCache(subtask.AgvId)
		if !r.StatusIsExists() {
			log.Errorf("robot status is nil")
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: codes.ErrDeviceStatusNotExists.GetCode(), Message: codes.ErrDeviceStatusNotExists.Error()}, nil
			//return codes.ErrDeviceStatusNotExists
		}
		if err := m.mc.TRM.ClearRobotPathAndLockNode(ctxTx, r, subtask.MapName, getSiteId, r.Status.Angle, r.Type); err != nil {
			if errors.Is(err, codes.ErrLockConflict) {
				caller, _ := m.GetCaller(r.Account.AgvId)
				if caller == nil {
					log.Infof("没有找到小车连接")
					ctxTx.Rollback()
					return &proto.ReplyPb{Code: codes.ErrRobotNotExists.GetCode(), Message: codes.ErrRobotNotExists.Error()}, nil

				}
				if errAlarm := caller.SendBlockConflictAlarm(ctxTx, int(request.GetSiteId())); errAlarm != nil {
					ctxTx.Rollback()
					return &proto.ReplyPb{Code: errAlarm.GetCode(), Message: errAlarm.Error()}, nil
				}

				if subtask.SubStatus == model.WalkTaskClearing {
					log.Infof("更新subtask:%d 子状态为任务清除完成", subtask.Id)
					errWrite := m.mc.TM.UpdateSubtaskKeyToDB(ctxTx, subtask.Id, "sub_status", model.WalkTaskCleared)
					if errWrite != nil {
						return &proto.ReplyPb{Code: errWrite.GetCode(), Message: errWrite.Error()}, nil
					}
					log.Infof("成功更新subtask:%d 子状态为任务清除完成", subtask.Id)
				}
				ctxTx.Commit()
				return &proto.ReplyPb{Code: 0, Message: "success"}, nil
			}
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}
		if subtask.SubStatus == model.WalkTaskClearing {
			log.Infof("更新subtask:%d 子状态为任务清除完成", subtask.Id)
			errWrite := m.mc.TM.UpdateSubtaskKeyToDB(ctxTx, subtask.Id, "sub_status", model.WalkTaskCleared)
			if errWrite != nil {
				return &proto.ReplyPb{Code: errWrite.GetCode(), Message: errWrite.Error()}, nil
			}
			log.Infof("成功更新subtask:%d 子状态为任务清除完成", subtask.Id)
		}
	}
	ctxTx.Commit()
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}

//小车智能上线
func (m *DispatchManager) SmartTaskOnline(ctx context.Context, request *proto.OnlineRequest) (*proto.ReplyPb, error) {
	// fmt.Printf("robot:%s SmartTaskOnline projectId:%s trafficMapName:%s site:%d\n", request.GetAgvId(), request.GetProjectId(), request.GetTrafficMapName(), request.GetSiteId())
	log.Warnf("robot:%s SmartTaskOnline projectId:%s trafficMapName:%s site:%d", request.GetAgvId(), request.GetProjectId(), request.GetTrafficMapName(), request.GetSiteId())

	//先判断请求上线车是否存在取消中和完成中
	runningSubtask, _ := m.mc.TM.GetUnCancelOrFinshSubtaskFromDB(ctx, request.GetAgvId())
	//取消中，完成中
	if runningSubtask != nil && (runningSubtask.Status == model.TaskStatusCanceling || runningSubtask.Status == model.TaskStatusInFinish) {
		var status model.TaskStatus
		if runningSubtask.Status == model.TaskStatusCanceling {
			status = model.TaskStatusCanceled
		} else if runningSubtask.Status == model.TaskStatusInFinish {
			status = model.TaskStatusFinish
		}

		//将数据制成已取消 或者 已完成
		ctxTx := boot.Begin(ctx)
		if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, runningSubtask.Id, status, false); err != nil {
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}
		if err1 := m.mc.TM.DeleteSubtasksToDB(ctxTx, runningSubtask.TaskId); err1 != nil {
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err1.GetCode(), Message: err1.Error()}, nil
		}
		ctxTx.Commit()
	}

	r := robotmanager.GetFromCache(request.GetAgvId())
	if r == nil {
		log.Warnf("车辆智能上线失败，未找到小车  robot:%s", request.GetAgvId())
		return &proto.ReplyPb{Code: codes.ErrRobotNotExists.GetCode(), Message: codes.ErrRobotNotExists.Error()}, nil
	}
	if !r.Account.IsEnable {
		log.Warnf("车辆智能上线失败，小车未启用 ,同时更新任务在线状态为=false robot:%s", request.GetAgvId())
		robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, false)
		return &proto.ReplyPb{Code: codes.ErrRobotDisable.GetCode(), Message: codes.ErrRobotDisable.Error()}, nil
	}
	if !r.StatusIsExists() || !r.Status.Online {
		log.Warnf("车辆智能上线失败，小车status is null ,online=false 同时更新任务在线状态=false  robot:%s", request.GetAgvId())
		robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, false)
		return &proto.ReplyPb{Code: codes.ErrRobotStatusNotMatch.GetCode(), Message: codes.ErrRobotStatusNotMatch.Error()}, nil
	}
	map_name := m.mc.TRM.GetMapName(request.ProjectId, request.TrafficMapName)
	if map_name == "" {
		return &proto.ReplyPb{Code: codes.ErrMapNotFound.GetCode(), Message: codes.ErrMapNotFound.Error()}, nil
	} else if map_name != r.Account.MapName {
		return &proto.ReplyPb{Code: codes.ErrMapNameNotMatch.GetCode(), Message: codes.ErrMapNameNotMatch.Error()}, nil
	}
	map_model, errGetMap := m.mc.TRM.GetMapFromDB(ctx, map_name)
	if errGetMap != nil {
		log.ErrorfWithContext(ctx, "switch map error, get map error:%s", errGetMap.Error())
		return &proto.ReplyPb{Code: codes.ErrMapNotFound.GetCode(), Message: codes.ErrMapNotFound.Error()}, nil
	}

	var site string
	angle := request.GetAngle()
	if request.GetSiteId() == 0 {
		site = map_model.MapData.NeareastOnlineSite(r, m.SystemConfig)
		if site == "0" {
			return &proto.ReplyPb{Code: codes.ErrRobotOffse.GetCode(), Message: codes.ErrRobotOffse.Error()}, nil
		}
		node, finde := map_model.MapData.NodesMap[site]
		if !finde {
			return &proto.ReplyPb{Code: codes.ErrMapNodeNotFound.GetCode(), Message: codes.ErrMapNodeNotFound.Error()}, nil
		}
		if node.Type == model.NodeCharge || node.Type == model.NodeAutoDoor ||
			node.Type == model.NodeConveyorLine || node.Type == model.NodeElevator ||
			node.Type == model.NodeHoist || node.Type == model.NodeManipulator {
			//log.Warnf("%d点位为特殊点位，不可上线", site)
			fmt.Printf("%s点位为特殊点位，不可上线\n", site)
			return &proto.ReplyPb{Code: codes.ErrNodeIsSpecial.GetCode(), Message: codes.ErrNodeIsSpecial.Error()}, nil
		}
	} else {
		site = strconv.Itoa(int(request.GetSiteId()))
		angle2, err := map_model.MapData.GetNeareastOnlineSiteAngle(r, site)
		if err != nil {
			return &proto.ReplyPb{Code: codes.ErrMapNodeNotFound.GetCode(), Message: codes.ErrRobotNotExists.Error()}, nil
		}
		angle = int32(angle2)
	}
	//判断上线层与当前待执行任务的目标地图对比
	isOccupyElevetor := false
	currentDoingTask, _ := m.mc.TM.GetRunningSubtaskFromDB(ctx, r.Account.AgvId)
	if currentDoingTask == nil {
		currentDoingTask, _ = m.mc.TM.GetWaitingSubtaskFromDB(ctx, r.Account.AgvId)
	}
	if currentDoingTask != nil {
		m.mc.TM.UpdateSubtaskKeyToDB(ctx, currentDoingTask.Id, "task_operate_flag", 0)
		if currentDoingTask.NextSwitchType == model.FloorRelation && currentDoingTask.NextSwitchDeviceCopy != "" {
			//占用不释放
			isOccupyElevetor = true
		} else if currentDoingTask.SwitchType == model.FloorRelation && currentDoingTask.SwitchDeviceCopy != "" {
			//在目标地图可以释放
			if r.Status.MapName == currentDoingTask.MapName {
				isOccupyElevetor = true
				getSiteId := strconv.Itoa(int(request.GetSiteId()))
				//如果小车上线点不等于跨楼层电梯点&&电梯还处于占用状态=>释放占用
				if r.Status.SiteIdS != "0" && getSiteId != currentDoingTask.SwitchToSite && m.mc.DM.GetElevatorInCache(ctx, currentDoingTask.SwitchDeviceCopy).AgvId != "" && m.mc.DM.GetElevatorInCache(ctx, currentDoingTask.SwitchDeviceCopy).AgvId == r.Account.AgvId {
					//释放电梯占用
					log.Warnf("robot %s is in goalMap online,but not release elevator,can close and release", request.GetAgvId())
					m.mc.DM.Close(currentDoingTask.SwitchDeviceCopy, r.Account.AgvId, currentDoingTask.MapName, currentDoingTask.TaskId)
					m.mc.DM.ModifyElevatorOccupyToDB(ctx, currentDoingTask.SwitchDeviceCopy, "")
				}
			}
		}
	}
	if err := m.mc.TRM.LockNodeAround(ctx, r, map_name, site, int(angle), r.Type, isOccupyElevetor, true); err != nil {
		log.ErrorfWithContext(ctx, "robot:%s online error:%s", request.GetAgvId(), err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}

	//人工锁的点--
	lockSites, err := m.TRM().GetAllLockSiteFromDB(ctx)
	if err != nil {
		log.ErrorfWithContext(ctx, "get locked sites error:%s", err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	for _, lockSite := range lockSites {
		if lockSite.MapName == map_name && lockSite.SiteId == site {
			return &proto.ReplyPb{Code: codes.ErrNodeLocked.GetCode(), Message: codes.ErrNodeLocked.Error()}, nil
		}
	}
	//找到当前AGV绑定的主任务编号--
	bindMainTaskCode := ""
	if currentDoingTask != nil {
		bindMainTaskCode = currentDoingTask.MainTaskCode
	}
	// bindMainTask, errb := m.mc.TM.GetMainTaskByAgvFromDB(ctx, request.GetAgvId())
	// if errb != nil {
	// 	log.Errorf("车辆上线失败，robot:%s,查询绑定主任务编号失败！！", request.GetAgvId())
	// 	return &proto.ReplyPb{Code: errb.GetCode(), Message: errb.Error()}, nil
	// }
	//第三方交互区域
	lockAreas, err := m.TRM().GetAllLockAreaFromDB(ctx)
	if err != nil {
		log.ErrorfWithContext(ctx, "get locked Areas error:%s", err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	for _, lockArea := range lockAreas {
		if lockArea.MapName == map_name {
			//当前车的
			if lockArea.MainTaskCode != "" && bindMainTaskCode != "" && lockArea.MainTaskCode == bindMainTaskCode {
				continue
			}
			for _, siteId := range lockArea.ContainDotIds {
				if site == siteId {
					return &proto.ReplyPb{Code: codes.ErrNodeLocked.GetCode(), Message: codes.ErrNodeLocked.Error()}, nil
				}
			}
		}
	}
	//上线可以清除机器人全局路径和局部路径
	ctxTx := boot.Begin(ctx)
	//1、清除全局路径
	errGlobalPathClear := m.TRM().DeleteRobotGlobalPaths(ctxTx, request.GetAgvId(), map_name)
	if err != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: errGlobalPathClear.GetCode(), Message: errGlobalPathClear.Error()}, nil
	}
	//2、清除局部路径(只清除路径和路径锁，不清除锁特殊锁)
	errLocalPathClear := m.TRM().ClearRobotLocalPaths(ctxTx, request.GetAgvId(), map_name)
	if err != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: errLocalPathClear.GetCode(), Message: errLocalPathClear.Error()}, nil
	}
	//释放待命点
	errFreeStandByPoint := m.TRM().FreeRobotStandByPoint(ctxTx, request.GetAgvId())
	if errFreeStandByPoint != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: errFreeStandByPoint.GetCode(), Message: errFreeStandByPoint.Error()}, nil
	}
	ctxTx.Commit()
	log.Infof("小车智能上线处理成功  robot:%s site:%s", request.GetAgvId(), site)
	site_int, _ := strconv.Atoi(site)
	return &proto.ReplyPb{Code: 0, Message: "success", OnlineId: uint32(site_int)}, nil
}

//小车任务上线
func (m *DispatchManager) TaskOnline(ctx context.Context, request *proto.OnlineRequest) (*proto.ReplyPb, error) {
	log.Warnf("robot:%s online signal:%s:%s:%d", request.GetAgvId(), request.GetProjectId(), request.GetTrafficMapName(), request.GetSiteId())
	//先判断请求上线车是否存在取消中和完成中
	runningSubtask, _ := m.mc.TM.GetUnCancelOrFinshSubtaskFromDB(ctx, request.GetAgvId())

	//取消中，完成中
	if runningSubtask != nil && (runningSubtask.Status == model.TaskStatusCanceling || runningSubtask.Status == model.TaskStatusInFinish) {
		var status model.TaskStatus
		if runningSubtask.Status == model.TaskStatusCanceling {
			status = model.TaskStatusCanceled
		} else if runningSubtask.Status == model.TaskStatusInFinish {
			status = model.TaskStatusFinish
		}

		//将数据制成已取消 或者 已完成
		ctxTx := boot.Begin(ctx)
		if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, runningSubtask.Id, status, false); err != nil {
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
		}

		//先修改其余子任务状态为已完成
		if err1 := m.mc.TM.DeleteSubtasksToDB(ctxTx, runningSubtask.TaskId); err1 != nil {
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: err1.GetCode(), Message: err1.Error()}, nil
		}

		//清
		ctxTx.Commit()
	}

	getSiteId := strconv.Itoa(int(request.GetSiteId()))

	map_name := m.mc.TRM.GetMapName(request.ProjectId, request.TrafficMapName)
	if map_name == "" {
		return &proto.ReplyPb{Code: codes.ErrMapNotFound.GetCode(), Message: codes.ErrMapNotFound.Error()}, nil
	}
	lockSites, err := m.TRM().GetAllLockSiteFromDB(ctx)
	if err != nil {
		log.ErrorfWithContext(ctx, "get locked sites error:%s", err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	for _, lockSite := range lockSites {
		if lockSite.MapName == map_name && lockSite.SiteId == getSiteId {
			return &proto.ReplyPb{Code: codes.ErrNodeLocked.GetCode(), Message: codes.ErrNodeLocked.Error()}, nil
		}
	}

	r := robotmanager.GetFromCache(request.GetAgvId())
	if r == nil {
		log.Errorf("车辆上线失败，未找到小车  robot:%s", request.GetAgvId())
		return &proto.ReplyPb{Code: codes.ErrRobotNotExists.GetCode(), Message: codes.ErrRobotNotExists.Error()}, nil
	}
	if !r.Account.IsEnable {
		log.Errorf("车辆上线失败，小车未启用  同时更新任务在线状态=false  robot:%s", request.GetAgvId())
		robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, false)
		return &proto.ReplyPb{Code: codes.ErrRobotDisable.GetCode(), Message: codes.ErrRobotDisable.Error()}, nil
	}
	//判断上线层与当前待执行任务的目标地图对比
	isOccupyElevetor := false
	currentDoingTask, _ := m.mc.TM.GetRunningSubtaskFromDB(ctx, r.Account.AgvId)
	if currentDoingTask == nil {
		currentDoingTask, _ = m.mc.TM.GetWaitingSubtaskFromDB(ctx, r.Account.AgvId)
	}
	//找到当前AGV绑定的主任务编号--
	bindMainTaskCode := ""
	if currentDoingTask != nil {
		m.mc.TM.UpdateSubtaskKeyToDB(ctx, currentDoingTask.Id, "task_operate_flag", 0)
		bindMainTaskCode = currentDoingTask.MainTaskCode
		//判断当前任务存在目标层的厂区切换点
		if currentDoingTask.NextSwitchType == model.FloorRelation && currentDoingTask.NextSwitchDeviceCopy != "" {
			//占用不释放
			isOccupyElevetor = true
		} else if currentDoingTask.SwitchType == model.FloorRelation && currentDoingTask.SwitchDeviceCopy != "" {
			//在目标地图可以释放
			if map_name == currentDoingTask.MapName {
				isOccupyElevetor = true
				//如果小车上线点不等于跨楼层电梯点&&电梯还处于占用状态(是当前车占用)=>释放占用
				if getSiteId != "0" && getSiteId != currentDoingTask.SwitchToSite && m.mc.DM.GetElevatorInCache(ctx, currentDoingTask.SwitchDeviceCopy).AgvId != "" && m.mc.DM.GetElevatorInCache(ctx, currentDoingTask.SwitchDeviceCopy).AgvId == r.Account.AgvId {
					//释放电梯占用
					log.Warnf("robot %s is in goalMap online,but not in elevator station (%s)!!!,", request.GetAgvId(), getSiteId)
					m.mc.DM.Close(currentDoingTask.SwitchDeviceCopy, r.Account.AgvId, currentDoingTask.MapName, currentDoingTask.TaskId)
					m.mc.DM.ModifyElevatorOccupyToDB(ctx, currentDoingTask.SwitchDeviceCopy, "")
				}
			}
		}
	}
	//第三方交互区域
	lockAreas, err := m.TRM().GetAllLockAreaFromDB(ctx)
	if err != nil {
		log.ErrorfWithContext(ctx, "get locked Areas error:%s", err.Error())
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	for _, lockArea := range lockAreas {
		if lockArea.MapName == map_name {
			//当前车的
			if lockArea.MainTaskCode != "" && bindMainTaskCode != "" && lockArea.MainTaskCode == bindMainTaskCode {
				continue
			}
			for _, siteId := range lockArea.ContainDotIds {
				if getSiteId == siteId {
					return &proto.ReplyPb{Code: codes.ErrNodeLocked.GetCode(), Message: codes.ErrNodeLocked.Error()}, nil
				}
			}
		}
	}

	if err := m.mc.TRM.LockNodeAround(ctx, r, map_name, getSiteId, int(request.GetAngle()), r.Type, isOccupyElevetor, true); err != nil {
		log.ErrorfWithContext(ctx, "robot:%s online error:%s", request.GetAgvId(), err.Error())
		if errors.Is(err, codes.ErrLockConflict) {
			caller, _ := m.GetCaller(r.Account.AgvId)
			ctxCall, cancel := context.WithTimeout(ctx, 10*time.Second)
			defer cancel()
			for caller == nil {
				log.Warnf("get robot:%s call failed,try once again", request.GetAgvId())
				select {
				case <-ctxCall.Done():
					log.Errorf("车辆上线失败，小车锁点失败1  同时更新任务在线状态=false  robot:%s", request.GetAgvId())
					robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, false)
					return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
				default:
					tools.Sleep(0.2)
					caller, _ = m.GetCaller(r.Account.AgvId)
				}
			}

			caller, _ = m.GetCaller(r.Account.AgvId)
			errAlarm := caller.SendBlockConflictAlarm(ctx, int(request.GetSiteId()))
			for errAlarm != nil {
				log.Warnf("send lock alarm to robot:%s failed,try once again", request.GetAgvId())
				select {
				case <-ctxCall.Done():
					log.Errorf("车辆上线失败，小车锁点失败2   同时更新任务在线状态=false robot:%s", request.GetAgvId())
					robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, false)
					return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
				default:
					tools.Sleep(0.2)
					errAlarm = caller.SendBlockConflictAlarm(ctx, int(request.GetSiteId()))
				}
			}
		}
		log.Errorf("车辆上线失败，小车锁点失败3  同时更新任务在线状态=false robot:%s", request.GetAgvId())
		robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, false)
		return &proto.ReplyPb{Code: err.GetCode(), Message: err.Error()}, nil
	}
	//上线可以清除机器人全局路径和局部路径
	ctxTx := boot.Begin(ctx)
	//1、清除全局路径
	errGlobalPathClear := m.TRM().DeleteRobotGlobalPaths(ctxTx, request.GetAgvId(), map_name)
	if err != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: errGlobalPathClear.GetCode(), Message: errGlobalPathClear.Error()}, nil
	}
	//2、清除局部路径(只清除路径和路径锁，不清除锁特殊锁)
	errLocalPathClear := m.TRM().ClearRobotLocalPaths(ctxTx, request.GetAgvId(), map_name)
	if err != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: errLocalPathClear.GetCode(), Message: errLocalPathClear.Error()}, nil
	}
	//3、释放待命点
	//先判断上线点是否在占用
	standbySites, errFreeStandByPoint := m.TRM().GetStandbySites(ctxTx)
	if errFreeStandByPoint != nil {
		ctxTx.Rollback()
		return &proto.ReplyPb{Code: errFreeStandByPoint.GetCode(), Message: errFreeStandByPoint.Error()}, nil
	}
	isCurrentOccupyStandBy := false
	for _, value := range standbySites {
		if value.AgvID == request.GetAgvId() && value.SiteId == getSiteId {
			isCurrentOccupyStandBy = true
			break
		}
	}
	if !isCurrentOccupyStandBy {
		errFreeStandByPoint = m.TRM().FreeRobotStandByPoint(ctxTx, request.GetAgvId())
		if errFreeStandByPoint != nil {
			ctxTx.Rollback()
			return &proto.ReplyPb{Code: errFreeStandByPoint.GetCode(), Message: errFreeStandByPoint.Error()}, nil
		}
	}
	ctxTx.Commit()
	m.RobotOnlineTaskAutoRetry(r)
	log.Info("车辆上线成功  更新任务在线状态true  robot:", request.GetAgvId())
	//下发配置到小车
	// m.SendConfigToRobots(ctx, &r.Type)
	robotmanager.UpdateTaskOnlineStatusToCache(r.Account.AgvId, true)
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}

//小车上报外设数据
func (m *DispatchManager) UploadSensorData(ctx context.Context, request *proto.SensorData) (*proto.ReplyPb, error) {
	go func() {
		httpClient := http.Client{Timeout: 5 * time.Second}
		map_name := m.mc.TRM.GetMapName(request.ProjectId, request.TrafficMapName)
		address := fmt.Sprintf("http://%s:%d/efork_api/upload_sensor_data", m.SystemConfig.MiddlewareCfg.Ip, m.SystemConfig.MiddlewareCfg.PortApi)
		data := strings.NewReader(json.DumpString(gin.H{"agv_id": request.GetAgvId(), "map_name": map_name, "sensor_type": request.GetSensorType(), "sensor_data": request.GetSensorData()}))
		_, err := httpClient.Post(address, "application/json", data)
		if err != nil {
			log.Errorf("send sensor data middleware error:%s", err.Error())
		}
	}()
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}

//AGV上传本地绘制的交通图到ＲＭＳ
func (m *DispatchManager) UploadTrafficMap(ctx context.Context, request *proto.Map) (*proto.ReplyPb, error) {

	map_name := fmt.Sprintf("%s@%s", request.GetProjectId(), request.GetTrafficMapName()) //m.mc.TRM.GetMapName(request.ProjectId, request.TrafficMapName)
	if map_name == "" {
		return &proto.ReplyPb{Code: codes.ErrMapExists.GetCode(), Message: codes.ErrMapExists.Error()}, nil
	}
	var RawBase64 base64.Data = base64.Data(request.MapData)

	mapData, mapXml, errParse := model.ParseMap(RawBase64.MustDecode())
	if errParse != nil {
		log.ErrorfWithContext(ctx, "map data parse error:%s", errParse.Error())
		return nil, codes.ErrInvalidParams.Wrap(errParse)
	}

	if map_name != mapData.MapInfo.MapName ||
		map_name != fmt.Sprintf("%s@%s", mapData.MapInfo.ProjectId, mapData.MapInfo.TrafficName) {
		return &proto.ReplyPb{Code: codes.ErrMapNameNotMatch.GetCode(), Message: codes.ErrMapNameNotMatch.Error()}, nil
	}

	map_data := &model.Map{
		MapName:           map_name,
		ProjectId:         mapData.MapInfo.ProjectId,
		TrafficName:       mapData.MapInfo.TrafficName,
		MapData:           *mapData,
		RawBase64:         RawBase64,
		MapDataJsonString: json.DumpString(mapXml),
	}

	if errUpdate := m.TRM().SaveMapToDB(ctx, map_data); errUpdate != nil {
		return &proto.ReplyPb{Code: errUpdate.GetCode(), Message: errUpdate.Error()}, nil
	}
	send := []*model.Map{map_data}
	for _, caller := range m.GetAllCallers() {
		caller.SendMaps(ctx, send)
	}
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}
func (m *DispatchManager) UploadProjectList(ctx context.Context, request *proto.ProjectList) (*proto.ReplyPb, error) {
	r := robotmanager.GetFromCache(request.GetAgvId())
	if r == nil {
		reply := new(proto.ReplyPb)
		reply.Code = codes.ErrRobotNotExists.GetCode()
		reply.Message = codes.ErrRobotNotExists.Error()
		return reply, nil
	}
	project_list := request.ProjectName
	// log.Infof("upload project size= %d", len(project_list))
	for _, project := range project_list {
		// log.Infof("project = %s", project)
		if project == "" {
			continue
		}
		m.projectNameList[project] = project
	}
	return &proto.ReplyPb{Code: 0, Message: "success"}, nil
}
